using Microsoft.CSharp.RuntimeBinder;
using DapperExtensionsNew.Att;
using DapperExtensionsNew.Configuration;
using DapperExtensionsNew.Data.Attributes;
using DapperExtensionsNew.Data.Descriptors;
using DapperExtensionsNew.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace DapperExtensionsNew
{
    internal static class TableCache
    {
        private static readonly ConcurrentDictionary<Type, DapperExtensionsNew.Data.Attributes.TableAttribute> Tables = new ConcurrentDictionary<Type, DapperExtensionsNew.Data.Attributes.TableAttribute>();

        private static readonly ConcurrentDictionary<Type, List<ColumnDescriptor>> TableColumns = new ConcurrentDictionary<Type, List<ColumnDescriptor>>();

        public static TableDescriptor GetTableDescriptor(Type typeOfModel)
        {
            DapperExtensionsNew.Data.Attributes.TableAttribute table = GetTable(typeOfModel);
            List<ColumnDescriptor> columnDescriptors = GetColumnDescriptors(typeOfModel);
            SqlDatabaseOptions sqlDatabaseOptions = SqlDatabaseOptionsFactory.GetSqlDatabaseOptions(table.Analyzer);
            return new TableDescriptor
            {
                TableName = table.Name,
                Analyzer = table.Analyzer,
                Db = table.Db,
                ColumnDescriptors = columnDescriptors,
                Options = sqlDatabaseOptions
            };
        }

        public static List<ColumnDescriptor> GetColumnDescriptors(Type typeOfModel)
        {
            return TableColumns.GetOrAdd(typeOfModel, delegate (Type n)
            {
                PropertyInfo[] properties = n.GetProperties();
                List<ColumnDescriptor> list = new List<ColumnDescriptor>(properties.Length);
                PropertyInfo[] array = properties;
                foreach (PropertyInfo propertyInfo in array)
                {
                    if (propertyInfo.PropertyType.IsSimpleType() && IsMapped(propertyInfo))
                    {
                        DapperExtensionsNew.Data.Attributes.ColumnAttribute columnAttribute = GetColumnAttribute(propertyInfo);
                        bool isPrimaryKey = false;
                        bool autoIncrement = false;
                        Action<object, object> setKeyValue = null;
                        Func<object, object> getKeyValue = null;
                        DapperExtensionsNew.Data.Attributes.KeyAttribute keyAttribute = GetKeyAttribute(propertyInfo);
                        if (string.Equals(propertyInfo.Name, "Id", StringComparison.OrdinalIgnoreCase) && keyAttribute == null)
                        {
                            keyAttribute = new DapperExtensionsNew.Data.Attributes.KeyAttribute
                            {
                                AutoIncrement = true
                            };
                        }
                        if (keyAttribute != null)
                        {
                            isPrimaryKey = true;
                            autoIncrement = keyAttribute.AutoIncrement;
                            getKeyValue = MethodInfoUtil.MakeFastPropertyGetter(propertyInfo);
                            setKeyValue = MethodInfoUtil.MakeFastPropertySetter(n, propertyInfo);
                        }
                        ColumnDescriptor columnDescriptor = new ColumnDescriptor
                        {
                            IsPrimaryKey = isPrimaryKey,
                            AutoIncrement = autoIncrement,
                            SetKeyValue = setKeyValue,
                            GetKeyValue = getKeyValue,
                            Name = propertyInfo.Name,
                            DbName = propertyInfo.Name,
                            NotUpdate = IsNotUpdate(propertyInfo),
                            TypeName = propertyInfo.PropertyType.Name
                        };
                        if (columnAttribute != null && columnAttribute.Name != null)
                        {
                            columnDescriptor.DbName = columnAttribute.Name;
                        }
                        columnDescriptor.IsDeletedKey = IsDeletedKey(columnDescriptor.DbName);
                        if (columnDescriptor.IsDeletedKey)
                        {
                            columnDescriptor.IsNotGenWhereCondition = IsNotGenWhereCondition(propertyInfo);
                        }
                        list.Add(columnDescriptor);
                    }
                }
                if (list.Count == 0)
                {
                    throw new ArgumentException(n.FullName + " no cols");
                }
                return list;
            });
        }

        private static DapperExtensionsNew.Data.Attributes.KeyAttribute GetKeyAttribute(PropertyInfo propertyInfo)
        {
            DapperExtensionsNew.Data.Attributes.KeyAttribute keyAttribute = propertyInfo.GetCustomAttributes(typeof(DapperExtensionsNew.Data.Attributes.KeyAttribute), inherit: true).FirstOrDefault() as DapperExtensionsNew.Data.Attributes.KeyAttribute;
            if (keyAttribute == null)
            {
                dynamic val = propertyInfo.GetCustomAttributes(inherit: true).FirstOrDefault((attr) => attr.GetType().Name == typeof(DapperExtensionsNew.Data.Attributes.KeyAttribute).Name);
                if (val != null)
                {
                    return new DapperExtensionsNew.Data.Attributes.KeyAttribute();
                }
            }
            return keyAttribute;
        }

        private static bool IsDeletedKey(string columnName)
        {
            if (columnName == "IsDeleted" || columnName == "is_deleted")
            {
                return true;
            }
            return false;
        }

        private static bool IsNotUpdate(PropertyInfo propertyInfo)
        {
            return propertyInfo.GetCustomAttributes(inherit: true).Any((attr) => attr.GetType().Name == typeof(NotUpdateAttribute).Name);
        }

        private static DapperExtensionsNew.Data.Attributes.ColumnAttribute GetColumnAttribute(PropertyInfo propertyInfo)
        {
            DapperExtensionsNew.Data.Attributes.ColumnAttribute columnAttribute = propertyInfo.GetCustomAttributes(typeof(DapperExtensionsNew.Data.Attributes.ColumnAttribute), inherit: true).FirstOrDefault() as DapperExtensionsNew.Data.Attributes.ColumnAttribute;
            if (columnAttribute == null)
            {
                dynamic val = propertyInfo.GetCustomAttributes(inherit: true).FirstOrDefault((object attr) => attr.GetType().Name == typeof(DapperExtensionsNew.Data.Attributes.ColumnAttribute).Name);
                if (val != null)
                {
                    DapperExtensionsNew.Data.Attributes.ColumnAttribute columnAttribute2 = new DapperExtensionsNew.Data.Attributes.ColumnAttribute();
                    columnAttribute2.Name = val.Name;
                    return columnAttribute2;
                }
            }
            return columnAttribute;
        }

        private static bool IsMapped(PropertyInfo pi)
        {
            object[] customAttributes = pi.GetCustomAttributes(inherit: true);
            if (customAttributes.Length != 0)
            {
                return !customAttributes.Any((x) => x.GetType() == typeof(IgnoreAttribute) || x.GetType().Name == typeof(NotMappedAttribute).Name);
            }
            return true;
        }

        private static bool IsNotGenWhereCondition(PropertyInfo pi)
        {
            object[] customAttributes = pi.GetCustomAttributes(inherit: true);
            if (customAttributes.Length != 0)
            {
                return customAttributes.Any((x) => x.GetType().Name == typeof(NotGenWhereConditionAttribute).Name);
            }
            return false;
        }

        public static DbType GetDialect(Type type)
        {
            return GetTable(type).Analyzer;
        }

        public static DapperExtensionsNew.Data.Attributes.TableAttribute GetTable(Type type)
        {
            return Tables.GetOrAdd(type, delegate (Type n)
            {
                DapperExtensionsNew.Data.Attributes.TableAttribute tableAttribute = n.GetCustomAttributes(typeof(DapperExtensionsNew.Data.Attributes.TableAttribute), inherit: true).FirstOrDefault() as DapperExtensionsNew.Data.Attributes.TableAttribute;
                return tableAttribute != null ? tableAttribute : DynamicGetTableAttribute(n);
            });
        }

        private static DapperExtensionsNew.Data.Attributes.TableAttribute DynamicGetTableAttribute(Type type)
        {
            dynamic val = type.GetCustomAttributes(inherit: true).FirstOrDefault((attr) => attr.GetType().Name == typeof(DapperExtensionsNew.Data.Attributes.TableAttribute).Name);
            if (val != null)
            {
                DapperExtensionsNew.Data.Attributes.TableAttribute tableAttribute = new DapperExtensionsNew.Data.Attributes.TableAttribute();
                tableAttribute.Name = val.Name;
                DapperExtensionsNew.Data.Attributes.TableAttribute tableAttribute2 = tableAttribute;
                try
                {
                    if (!(!string.IsNullOrEmpty(val.Schema) ? true : false))
                    {
                        return tableAttribute2;
                    }
                    tableAttribute2.Schema = val.Schema;
                    return tableAttribute2;
                }
                catch (RuntimeBinderException)
                {
                    return tableAttribute2;
                }
            }
            return new DapperExtensionsNew.Data.Attributes.TableAttribute
            {
                Name = type.Name
            };
        }
    }
}
